---
description: Getting Started with Account Abstraction in Viem
---

# Getting Started with Account Abstraction

**[Account Abstraction (ERC-4337)](https://eips.ethereum.org/EIPS/eip-4337)** is a proposal within the Ethereum ecosystem which aims to standardize Smart Contract Accounts (SCA) and their operations without the need to modify or upgrade the protocol.

Smart Contract Accounts can send calls on the Network via a "meta-transaction" called a **User Operation.** Users can send User Operations to **Bundlers** which aggregate User Operations into single transactions and submit them to the Network via an **EntryPoint** contract.

Key features that Account Abstraction enables are:

- **Batching:** Group multiple calls into a single transaction.
- **Fee Sponsorship:** Allow third parties to pay for gas fees, or pay for gas via ERC20 tokens.
- **Arbitrary Signature Verification:** Smart Contract Accounts can contain arbitrary signature verification logic (e.g. WebAuthn, secp256r1, secp256k1, ed25519, etc).
- **Multi-Owner Wallets:** Enable multiple owners to control a single account, and set rules for the owners.
- **Recovery Mechanisms:** A Smart Contract Account can assign multiple entities or services as trusted recovery agents for the Account.
- and many more...

:::note
**Compatibility Note**

As ERC-4337 is not enshrined on the protocol, this means that Smart Accounts are incompatible with Viem's Transaction APIs such as `sendTransaction` and `writeContract`.

Sending "transactions" can be achieved by broadcasting a **User Operation** to a **Bundler**, which will then broadcast it to the Network shortly after.

The most common Actions for **User Operations** are:

- [`sendUserOperation`](/account-abstraction/actions/bundler/sendUserOperation) (also supports [Contract Writes](/account-abstraction/actions/bundler/sendUserOperation#contract-calls))
- [`estimateUserOperationGas`](/account-abstraction/actions/bundler/estimateUserOperationGas)
- [`getUserOperation`](/account-abstraction/actions/bundler/getUserOperation)
- [`getUserOperationReceipt`](/account-abstraction/actions/bundler/getUserOperationReceipt)

Once Account Abstraction is enshrined on the protocol, we anticipate the above Actions will become redundant in favor of Viem's Transaction APIs.
:::

## Sending your first User Operation

### 1. Set up a Client

A Smart Account needs access to the Network to query for information about its state (e.g. nonce, address, etc). Let's set up a Client that we can use for the Smart Account:

```ts twoslash
// @noErrors
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})
```

[See `createPublicClient` Docs](/docs/clients/public)

### 2. Set up a Bundler Client

Next, we need to set up a Bundler Client. A Bundler is required to submit User Operations to the Network for the Smart Account.

```ts twoslash
import { createPublicClient, http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction' // [!code focus]
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = createBundlerClient({ // [!code focus]
  client, // [!code focus]
  transport: http('https://public.pimlico.io/v2/1/rpc'), // [!code focus]
}) // [!code focus]
```

:::info
The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using [Pimlico's Bundler](https://www.pimlico.io), [Biconomy's Bundler](https://www.biconomy.io), or another Bundler service.
:::

[See `createBundlerClient` Docs](/account-abstraction/clients/bundler)

### 3. Set up an Owner

We also need to set up an Owner for the Smart Account which will be used to sign User Operations (transactions) for the Smart Account.

```ts twoslash
// @noErrors
import { createPublicClient, http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts' // [!code focus]

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = createBundlerClient({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = privateKeyToAccount('0x...') // [!code focus]
```

[See `privateKeyToAccount` Docs](/docs/accounts/local/privateKeyToAccount)

### 4. Create a Smart Account

Next, we instantiate a Smart Account. For this example, we will use [`toCoinbaseSmartAccount`](/account-abstraction/accounts/smart/toCoinbaseSmartAccount) (Coinbase Smart Wallet).

```ts twoslash
// @noErrors
import { createPublicClient, http } from 'viem'
import { // [!code focus]
  createBundlerClient, // [!code focus]
  toCoinbaseSmartAccount // [!code focus]
} from 'viem/account-abstraction' // [!code focus]
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = createBundlerClient({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = privateKeyToAccount('0x...')

const account = await toCoinbaseSmartAccount({ // [!code focus]
  client, // [!code focus]
  owners: [owner], // [!code focus]
  version: '1.1', // [!code focus]
}) // [!code focus]
```

:::tip
**Tip:** `toCoinbaseSmartAccount` also accepts [Passkey (WebAuthn) Accounts](/account-abstraction/accounts/webauthn) as an `owner`.
:::

[See `toCoinbaseSmartAccount` Docs](/account-abstraction/accounts/smart/toCoinbaseSmartAccount)

### 5. Send User Operation

Next, we send a User Operation to the Bundler. For the example below, we will send 0.001 ETH to a random address.

```ts twoslash
import { createPublicClient, http, parseEther } from 'viem'
import { 
  createBundlerClient, 
  toCoinbaseSmartAccount 
} from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts' 

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const bundlerClient = createBundlerClient({
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const owner = privateKeyToAccount('0x...')

const account = await toCoinbaseSmartAccount({ 
  client, 
  owners: [owner],
  version: '1.1',
}) 

const hash = await bundlerClient.sendUserOperation({ // [!code focus]
  account, // [!code focus]
  calls: [{ // [!code focus]
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D', // [!code focus]
    value: parseEther('0.001') // [!code focus]
  }] // [!code focus]
}) // [!code focus]

const receipt = await bundlerClient.waitForUserOperationReceipt({ hash }) // [!code focus]
```

:::tip
**Tip:** The `calls` property also accepts [Contract Write calls](/account-abstraction/actions/bundler/sendUserOperation).
:::

[See `sendUserOperation` Docs](/account-abstraction/actions/bundler/sendUserOperation)

### 6. Optional: Hoist the Account

If you do not wish to pass an account around to every Action that requires an `account`, you can also hoist the account onto a Bundler Client.

```ts twoslash 
import { createPublicClient, http, parseEther } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts' 

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const owner = privateKeyToAccount('0x...')

const account = await toCoinbaseSmartAccount({ 
  client, 
  owners: [owner],
  version: '1.1',
}) 

const bundlerClient = createBundlerClient({
  account, // [!code ++]
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.sendUserOperation({
  account, // [!code --]
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: parseEther('0.001')
  }]
})
```

### 7. Optional: Sponsor User Operation 

By using a Paymaster, we can add sponsorship of User Operation fees.

Viem exposes a `paymaster` property on both the **Bundler Client** ("on Client" tab) and **User Operation Action** ("on Action" tab) to add User Operation sponsorship capabilities.

The `paymaster` property accepts a [Paymaster Client](/account-abstraction/clients/paymaster) ([among others](#TODO)), which is used to fetch the necessary data for User Operation sponsorship.

:::info
The example below uses [Pimlico's Paymaster API](https://docs.pimlico.io/infra/paymaster) – allowing consumers to sponsor gas fees for users on over 30+ chains.
:::

:::code-group

```ts twoslash [example.ts (on Client)]
import { http } from 'viem'
import { 
  createBundlerClient, 
  createPaymasterClient,
} from 'viem/account-abstraction'
import { account, client } from './config.ts'

const paymasterClient = createPaymasterClient({ // [!code ++]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++]
}) // [!code ++]

const bundlerClient = createBundlerClient({
  account,
  client,
  paymaster: paymasterClient, // [!code ++]
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.sendUserOperation({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: parseEther('0.001')
  }]
})
```

```ts twoslash [example.ts (on Action)]
import { http } from 'viem'
import { 
  createBundlerClient, 
  createPaymasterClient,
} from 'viem/account-abstraction'
import { account, client } from './config.ts'

const paymasterClient = createPaymasterClient({ // [!code ++]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code ++]
}) // [!code ++]

const bundlerClient = createBundlerClient({
  account,
  client,
  transport: http('https://public.pimlico.io/v2/1/rpc'),
})

const hash = await bundlerClient.sendUserOperation({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: parseEther('0.001')
  }]
  paymaster: paymasterClient, // [!code ++]
})
```

```ts twoslash [config.ts] filename="config.ts"
// @noErrors
import { createPublicClient, http, parseEther } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts' 

export const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const owner = privateKeyToAccount('0x...')

export const account = await toCoinbaseSmartAccount({ 
  client, 
  owners: [owner],
  version: '1.1',
}) 
```

:::

::::tip
If your Bundler also accepts Paymaster sponsorship (like [Pimlico](https://www.pimlico.io)), you can set `paymaster: true` instead of declaring a separate Paymaster Client.

:::code-group

```ts twoslash [example.ts (on Client)]
import { http } from 'viem'
import { 
  createBundlerClient, 
  createPaymasterClient,
} from 'viem/account-abstraction'
import { account, client } from './config.ts'

const paymasterClient = createPaymasterClient({ // [!code --]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code --]
}) // [!code --]

const bundlerClient = createBundlerClient({
  account,
  client,
  paymaster: paymasterClient, // [!code --]
  paymaster: true, // [!code ++]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'),
})
```

```ts twoslash [example.ts (on Action)]
import { http } from 'viem'
import { 
  createBundlerClient, 
  createPaymasterClient,
} from 'viem/account-abstraction'
import { account, client } from './config.ts'

const paymasterClient = createPaymasterClient({ // [!code --]
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'), // [!code --]
}) // [!code --]

const bundlerClient = createBundlerClient({
  account,
  client,
  transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}'),
})

const hash = await bundlerClient.sendUserOperation({
  calls: [{
    to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
    value: parseEther('0.001')
  }]
  paymaster: paymasterClient, // [!code --]
  paymaster: true, // [!code ++]
})
```

```ts twoslash [config.ts] filename="config.ts"
// @noErrors
import { createPublicClient, http, parseEther } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts' 

export const client = createPublicClient({
  chain: mainnet,
  transport: http(),
})

const owner = privateKeyToAccount('0x...')

export const account = await toCoinbaseSmartAccount({ 
  client, 
  owners: [owner],
  version: '1.1',
}) 
```

:::

::::
